#pragma once
#include <plog/Log.h>
#include <plog/Init.h>
#include <plog/Appenders/ConsoleAppender.h>
#include <plog/Appenders/RollingFileAppender.h>
#include <plog/Formatters/MessageOnlyFormatter.h>
#include <plog/Formatters/FuncMessageFormatter.h>
#include <plog/Formatters/TxtFormatter.h>
#include <plog/Formatters/CsvFormatter.h>
#include <cassert>
#ifdef __ANDROID__
	#include <plog/Appenders/AndroidAppender.h>
#endif

/*********** Raw C-Sytle && Android log print *************/
namespace {// can use even if `CLog::init()` is not called
	/**
	 * @brief raw original log print
	 * @param tag type: `const char*`,not std::string!
	 * @param msg type: `const char*`,not std::string!
	 * @example LogErr("Tag", "just a pure string message");
	 * @example LogErr("Tag", "Code %d, Msg: %s", 42, "failure");
	 * @example Print_E("Code %d, Msg: %s", 42, "failure");// not for AndroidEnv
	 */
	#ifdef __ANDROID__
		#include <android/log.h>
		#define LogErr(tag, fmt, ...) __android_log_print(ANDROID_LOG_ERROR, tag, fmt, ##__VA_ARGS__)
		#define LogWarn(tag, fmt, ...) __android_log_print(ANDROID_LOG_WARN, tag, fmt, ##__VA_ARGS__)
		#define LogInfo(tag, fmt, ...) __android_log_print(ANDROID_LOG_INFO, tag, fmt, ##__VA_ARGS__)
	#else
		#include <cstdio>
		#define LogErr(tag, fmt, ...) fprintf(stderr, "[ERROR][%s] " fmt "\n", tag, ##__VA_ARGS__)
		#define LogWarn(tag, fmt, ...) fprintf(stderr, "[WARN][%s] " fmt "\n", tag, ##__VA_ARGS__)
		#define LogInfo(tag, fmt, ...) fprintf(stdout, "[INFO][%s] " fmt "\n", tag, ##__VA_ARGS__)
	#endif
}// end namespace

#define _INIT_ if (!CLog::s_isInited) CLog::init(); else 

#define LogV _INIT_ PLOGV << "[V:] "
#define LogD _INIT_ PLOGD << "[D:] "
#define LogI _INIT_ PLOGI << "[I:] "
#define LogW _INIT_ PLOGW << "[W:] "
#define LogE _INIT_ PLOGE << "[E:] "
#define LogF _INIT_ PLOGF << "[F:] "
#define LogFile _INIT_ PLOGF_(1)

#define LogVV LogV << __FUNCTION__ << "@" << __LINE__ << ": "
#define LogDD LogD << __FUNCTION__ << "@" << __LINE__ << ": "
#define LogII LogI << __FUNCTION__ << "@" << __LINE__ << ": "
#define LogWW LogW << __FUNCTION__ << "@" << __LINE__ << ": "
#define LogEE LogE << __FUNCTION__ << "@" << __LINE__ << ": "
#define LogFF LogF << __FUNCTION__ << "@" << __LINE__ << ": "

class CLog {
public:
	/**
	* @example
	* None       => Hello World
	* HeaderFunc => main@60: Hello World
	* HeaderTxt  => 2025-07-11 14:28:01.603 DEBUG [6968] [main@29] Hello World
	* HeaderCsv  => 2025/07/11;15:25:51.381;DEBUG;28056;0000000000000000;main@18;"Hello World"
	*/
	enum class AppendHeaderType { None, HeaderFunc, HeaderTxt, HeaderCsv };

	/**@brief Log Level: verbose(cyan) -> debug(cyan) -> info(white) -> warning(yellow) -> error(red) -> fatal(white filled with red) -> none */
	enum class LogLevel {
		None = plog::none, Fatal = plog::fatal, Error = plog::error, Warning = plog::warning, Info = plog::info, Debug = plog::debug, Verbose = plog::verbose
	};

	/**@warning call `init` before using plog; do not: `static bool b = init();` XXX */
	static void init(AppendHeaderType appendHeader = AppendHeaderType::None, LogLevel logLevel = LogLevel::Verbose, const char* fileLogPath = "") {
		plog::IAppender* consoleAppender = nullptr;
		if (AppendHeaderType::None == appendHeader) {
			consoleAppender = new plog::ConsoleAppender<plog::MessageOnlyFormatter>;
		} else if (AppendHeaderType::HeaderFunc == appendHeader) {
			consoleAppender = new plog::ConsoleAppender<plog::FuncMessageFormatter>;
		} else if (AppendHeaderType::HeaderTxt == appendHeader) {
			consoleAppender = new plog::ConsoleAppender<plog::TxtFormatter>;
		} else if (AppendHeaderType::HeaderCsv == appendHeader) {
			consoleAppender = new plog::ConsoleAppender<plog::CsvFormatter>;
		}
		assert(consoleAppender != nullptr);

		auto _level = static_cast<plog::Severity>(static_cast<int>(logLevel));
		static plog::Logger<0>& Console_Logger = plog::init(_level);// Logger Instance0 - to Console
		Console_Logger.addAppender(consoleAppender);
		if (fileLogPath != "") {
			static plog::Logger<1>& File_Logger = plog::init<1>(_level);// Logger Instance1 - to File
			File_Logger.addAppender(new plog::RollingFileAppender<plog::TxtFormatter>(fileLogPath));// should append detail header info msg writting to file
		}
		#ifdef __ANDROID__
			const char* AndroidLogTag = "Android";// android already has detail log header msg, just use MessageOnlyFormatter
			static plog::AndroidAppender<plog::MessageOnlyFormatter> AndroidAppender = plog::AndroidAppender<plog::MessageOnlyFormatter>(AndroidLogTag);
			plog::init(_level, &AndroidAppender);
		#endif

		s_isInited = true;
	}

	static bool s_isInited;
};